/*
 * The contents of this web application are subject to the Mozilla Public License Version 
 * 1.1 (the "License"); you may not use this web application except in compliance with 
 * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
 * for the specific language governing rights and limitations under the License.
 * 
 * The Original Code is owned by and the Initial Developer of the Original Code is 
 * Composite A/S (Danish business reg.no. 21744409). All Rights Reserved
 * 
 * Section 11 of the License is EXPRESSLY amended to include a provision stating 
 * that any dispute, including but not limited to disputes related to the enforcement 
 * of the License, to which Composite A/S as owner of the Original Code, as Initial 
 * Developer or in any other role, becomes a part to shall be governed by Danish law 
 * and be initiated before the Copenhagen City Court ("K�benhavns Byret")            
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Workflow.Runtime;
using System.Xml.Linq;
using Composite.C1Console.Actions;
using Composite.C1Console.Security;
using Composite.C1Console.Workflow;
using Composite.C1Console.Workflow.Foundation;
using Composite.Core.ResourceSystem;
using Composite.Core.Serialization;
using Composite.Data;
using Composite.Data.DynamicTypes;
using Composite.Data.GeneratedTypes;
using Composite.Data.ProcessControlled;
using Composite.Data.ProcessControlled.ProcessControllers.GenericPublishProcessController;


namespace Composite.C1Console.Trees.Workflows
{
    [EntityTokenLock]
    [AllowPersistingWorkflow(WorkflowPersistingType.Idle)]
    public sealed partial class GenericEditDataWorkflow : Composite.C1Console.Workflow.Activities.FormsWorkflow
    {
        [NonSerialized]
        private bool _doPublish;


        [NonSerialized]
        private DataTypeDescriptorFormsHelper _dataTypeDescriptorFormsHelper;

        [NonSerialized]
        private string _typeName;


        private DataTypeDescriptorFormsHelper FormsHelper
        {
            get
            {
                if (_dataTypeDescriptorFormsHelper == null)
                {
                    if (String.IsNullOrEmpty(this.Payload))
                    {
                        throw new InvalidOperationException("Action token's 'Payload' property is null or empty");
                    }

                    Dictionary<string, string> serializedValues = StringConversionServices.ParseKeyValueCollection(this.Payload);

                    string iconResourceName = StringConversionServices.DeserializeValueString(serializedValues["_IconResourceName_"]);

                    string customFormMarkupPath = null;
                    if (serializedValues.ContainsKey("_CustomFormMarkupPath_"))
                    {
                        customFormMarkupPath = StringConversionServices.DeserializeValueString(serializedValues["_CustomFormMarkupPath_"]);
                    }

                    DataEntityToken dataEntityToken = this.EntityToken as DataEntityToken;
                    Verify.IsNotNull(dataEntityToken, "The given entity token is of wrong type");

                    Type interfaceType = dataEntityToken.InterfaceType;
                    

                    DataTypeDescriptor dataTypeDescriptor = DynamicTypeManager.GetDataTypeDescriptor(interfaceType);
                    Verify.IsNotNull(dataTypeDescriptor, "Can not find the type descriptor for the type '{0}'", interfaceType);

                    _typeName = dataTypeDescriptor.Name;

                    var generatedTypesHelper = new GeneratedTypesHelper(dataTypeDescriptor) { AllowForeignKeyEditing = true };

                    _dataTypeDescriptorFormsHelper = new DataTypeDescriptorFormsHelper(dataTypeDescriptor, true, this.EntityToken);
                    if (!string.IsNullOrEmpty(customFormMarkupPath))
                    {
                        _dataTypeDescriptorFormsHelper.CustomFormDefinition =
                            XDocument.Load(customFormMarkupPath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
                    }
                    _dataTypeDescriptorFormsHelper.LayoutIconHandle = iconResourceName;
                    _dataTypeDescriptorFormsHelper.AddReadOnlyFields(generatedTypesHelper.NotEditableDataFieldDescriptorNames);
                }

                return _dataTypeDescriptorFormsHelper;
            }
        }



        private string TypeName
        {
            get
            {
                return _typeName;
            }
        }



        public GenericEditDataWorkflow()
        {
            InitializeComponent();
        }



        private void editCodeActivity_DisplayForm_ExecuteCode(object sender, EventArgs e)
        {
            IData data = ((DataEntityToken)this.EntityToken).Data;

            if (!PermissionsFacade.GetPermissionsForCurrentUser(EntityToken).Contains(PermissionType.Publish) || !(data is IPublishControlled))
            {
                FormData formData = WorkflowFacade.GetFormData(InstanceId, true);

                if (formData.ExcludedEvents == null)
                    formData.ExcludedEvents = new List<string>();

                formData.ExcludedEvents.Add("SaveAndPublish");
            }

            if (data is IPublishControlled)
            {
                IPublishControlled publishControlledData = (IPublishControlled)data;
                if (publishControlledData.PublicationStatus == GenericPublishProcessController.Published)
                {
                    publishControlledData.PublicationStatus = GenericPublishProcessController.Draft;
                }
            }

            this.FormsHelper.UpdateWithBindings(data, this.Bindings);
            this.FormsHelper.LayoutLabel = data.GetLabel(true);

            this.DeliverFormData(
                    this.TypeName,
                    StandardUiContainerTypes.Document,
                    this.FormsHelper.GetForm(),
                    this.Bindings,
                    this.FormsHelper.GetBindingsValidationRules(data)
                );
        }



        private void saveCodeActivity_UpdateData_ExecuteCode(object sender, EventArgs e)
        {
            UpdateTreeRefresher updateTreeRefresher = this.CreateUpdateTreeRefresher(this.EntityToken);

            IData data = ((DataEntityToken)this.EntityToken).Data;

            bool isValid = ValidateBindings();
            if (!BindAndValidate(this.FormsHelper, data))
            {
                isValid = false;
            }

            var fieldsWithBrokenReferences = new List<string>();
            if (!data.TryValidateForeignKeyIntegrity(fieldsWithBrokenReferences))
            {
                isValid = false;

                foreach (string fieldName in fieldsWithBrokenReferences)
                {
                    ShowFieldMessage(fieldName, LocalizationFiles.Composite_Management.Validation_BrokenReference);
                }
            }

            if (isValid)
            {
                if (data is IPublishControlled)
                {
                    IPublishControlled publishControlledData = (IPublishControlled)data;
                    if (publishControlledData.PublicationStatus == GenericPublishProcessController.Published)
                    {
                        publishControlledData.PublicationStatus = GenericPublishProcessController.Draft;
                    }
                }

                DataFacade.Update(data);

                EntityTokenCacheFacade.ClearCache(EntityToken);

                updateTreeRefresher.PostRefreshMesseges(this.EntityToken);
                
                PublishIfNeeded(data);
            }

            SetSaveStatus(isValid);
        }


        private bool PublishIfNeeded(IData newData)
        {
            if (newData is IPublishControlled && _doPublish)
            {
                var actionToken = new GenericPublishProcessController.PublishActionToken();
                FlowControllerServicesContainer serviceContainer = WorkflowFacade.GetFlowControllerServicesContainer(WorkflowEnvironment.WorkflowInstanceId);
                ActionExecutorFacade.Execute(newData.GetDataEntityToken(), actionToken, serviceContainer);
                return true;
            }

            return false;
        }

        private void enablePublishCodeActivity_ExecuteCode(object sender, EventArgs e)
        {
            _doPublish = true;
        }
    }
}
